#pragma comment(lib,"Winmm.lib")
#pragma warning(disable : 4996)

#include "SerialPort.h"
#include <process.h>
#include <iostream>
#include <stdlib.h>


const uint32_t SLEEP_TIME_INTERVAL = 10;


CSerialPort::CSerialPort(void) {

    m_hComm = INVALID_HANDLE_VALUE;
}

CSerialPort::~CSerialPort(void)
{

}

bool CSerialPort::Open(uint32_t portNo /*= 1*/, uint32_t baud /*= CBR_9600*/, char parity /*= 'N'*/,
                       uint32_t databits/*= 8*/, uint32_t stopsbits /*= 1*/, DWORD dwCommEvents /*= EV_RXCHAR*/)
{

    char szDCBparam[50];
    sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits);
    char szPort[50];
    sprintf(szPort, "%s%d", "\\\\.\\COM", portNo);

    m_hComm = CreateFileA(szPort,
                          GENERIC_READ | GENERIC_WRITE,
                          0,
                          NULL,
                          OPEN_EXISTING,
                          0,
                          0);

    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        return false;
    }
    BOOL bIsSuccess = TRUE;
    COMMTIMEOUTS  CommTimeouts;
    CommTimeouts.ReadIntervalTimeout = 0;
    CommTimeouts.ReadTotalTimeoutMultiplier = 0;
    CommTimeouts.ReadTotalTimeoutConstant = 0;
    CommTimeouts.WriteTotalTimeoutMultiplier = 0;
    CommTimeouts.WriteTotalTimeoutConstant = 0;
    if (bIsSuccess)
    {
        bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts);
    }

    DCB  dcb;
    if (bIsSuccess)
    {

        DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, NULL, 0);
        wchar_t* pwText = new wchar_t[dwNum];
        if (!MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, pwText, dwNum))
        {
            bIsSuccess = TRUE;
        }

        bIsSuccess = GetCommState(m_hComm, &dcb);
        dcb.fRtsControl = RTS_CONTROL_ENABLE;
        delete[] pwText;
    }

    if (bIsSuccess)
    {
        bIsSuccess = SetCommState(m_hComm, &dcb);
    }

    PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

    printf("SerialPort listern on COM%d \n", portNo);
    return bIsSuccess == TRUE;
}



void CSerialPort::Close()
{
    if (m_hComm != INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hComm);
        m_hComm = INVALID_HANDLE_VALUE;
    }
}



uint32_t CSerialPort::GetBytesInCOM()
{
    DWORD dwError = 0;
    COMSTAT  comstat;
    memset(&comstat, 0, sizeof(COMSTAT));
    uint32_t BytesInQue = 0;
    if (ClearCommError(m_hComm, &dwError, &comstat))
    {
        BytesInQue = comstat.cbInQue;
    }
    return BytesInQue;
}




bool CSerialPort::ReadChar(char& cRecved)
{
    BOOL  bResult = TRUE;
    DWORD BytesRead = 0;
    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        return false;
    }
    bResult = ReadFile(m_hComm, &cRecved, 1, &BytesRead, NULL);
    if ((!bResult))
    {
        DWORD dwError = GetLastError();
        PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
        return false;
    }
    return (BytesRead == 1);

}



DWORD CSerialPort::Receive(BYTE* dstBuf, DWORD toRecvSize)
{
    DWORD errorFlags;
    COMSTAT comStat;
    OVERLAPPED osRead;
    DWORD bytesToRead;
    BOOL  readOk;
    DWORD recvedLen = 0;


    ClearCommError(m_hComm, &errorFlags, &comStat);
    if (comStat.cbInQue == 0) {
        return(0);
    }

    bytesToRead = __min(toRecvSize, comStat.cbInQue);
    memset(&osRead, 0x00, sizeof(OVERLAPPED));
    readOk = ReadFile(m_hComm, dstBuf, bytesToRead, &recvedLen, &osRead);
    if (readOk) {
        return(recvedLen);
    }
    else {
        if (GetLastError() == ERROR_IO_PENDING) {         
            GetOverlappedResult(m_hComm, &osRead, &recvedLen, TRUE); 
            return(recvedLen);
        }
        return(0);
    }

}



DWORD CSerialPort::Send(BYTE* srcBuf, DWORD toSendLen)
{
    OVERLAPPED osWrite;
    BOOL  writeOk;
    DWORD sentLen = 0;

    if (toSendLen == 0) {
        return(0);
    }

    memset(&osWrite, 0x00, sizeof(OVERLAPPED));
    writeOk = WriteFile(m_hComm, srcBuf, toSendLen, &sentLen, &osWrite);
    if (writeOk) {
        return(sentLen);
    }
    else {
        if (GetLastError() == ERROR_IO_PENDING) {        
            GetOverlappedResult(m_hComm, &osWrite, &sentLen, TRUE);    
            return(sentLen);
        }
        return(0);
    }
}

int CSerialPort::SetRts(int enable) {
    // 获取当前串口参数
    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (!GetCommState(m_hComm, &dcbSerialParams)) {
     //   std::cout << "获取串口参数失败" << std::endl;
        return -1;
    }
    // 启用或禁用流控制
    dcbSerialParams.fRtsControl  = enable;
    // 更新串口参数
    if (!SetCommState(m_hComm, &dcbSerialParams)) {
       // std::cout << "设置串口参数失败" << std::endl;
        return -1;
    }
    return 0;
   // std::cout << "流控制设置成功" << std::endl;
}

int CSerialPort::SetCts(int enable) {
    // 获取当前串口参数
    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (!GetCommState(m_hComm, &dcbSerialParams)) {
        //   std::cout << "获取串口参数失败" << std::endl;
        return -1;
    }
    // 启用或禁用流控制
    dcbSerialParams.fOutxCtsFlow   = enable;
    // 更新串口参数
    if (!SetCommState(m_hComm, &dcbSerialParams)) {
        // std::cout << "设置串口参数失败" << std::endl;
        return -1;
    }
    return 0;
    // std::cout << "流控制设置成功" << std::endl;
}
void CSerialPort::Run() {

    while (TRUE)
    {
        uint32_t BytesInQue = this->GetBytesInCOM();
        if (BytesInQue == 0)
        {
            Sleep(SLEEP_TIME_INTERVAL);
            continue;
        }
    }
    return;

}


BOOL CSerialPort::IsSendingCompleted(void)
{
    DWORD errorFlags;
    COMSTAT comStat;
    ClearCommError(m_hComm, &errorFlags, &comStat);
    return((comStat.cbOutQue == 0) ? TRUE : FALSE);
}